﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SingletonPattern
{
    /// <summary>
    /// 双重校验锁模式
    /// 线程安全模式对于每个线程都会对线程辅助对象locker加锁之后再判断实例是否存在，
    /// 对于这个操作完全没有必要的，
    /// 因为当第一个线程创建了该类的实例之后，后面的线程此时只需要直接判断（_uniqueInstance==null）为假，
    /// 此时完全没必要对线程辅助对象加锁之后再去判断，所以上面的实现方式增加了额外的开销，损失了性能，
    /// 为了改进上面实现方式的缺陷，我们只需要在lock语句前面加一句（_uniqueInstance==null）的判断就可以避免锁所增加的额外开销，
    /// 这种实现方式我们就叫它 “双重锁定”
    /// </summary>
    /// <remarks>
    /// </remarks>
    public sealed class TwinCheckSingleton
    {
        // 定义一个静态变量来保存类的实例
        private static TwinCheckSingleton _uniqueInstance = null;

        // 定义一个标识确保线程同步
        private static readonly object locker = new object();

        // 定义私有构造函数，使外界不能创建该类实例
        private TwinCheckSingleton()
        {
        }

        // 定义公有属性来提供一个全局访问点,同时你也可以定义公有方法来提供全局访问点
        public static TwinCheckSingleton Instance
        {
            get
            {
                // Double-Checked Locking
                if (null == _uniqueInstance)
                {
                    lock (locker)
                    {
                        if (null == _uniqueInstance)
                        {
                            _uniqueInstance = new TwinCheckSingleton();
                        }
                    }
                }
                return _uniqueInstance;
            }
        }

        /// <summary>
        /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
        /// </summary>
        /// <returns></returns>
        public static TwinCheckSingleton GetInstance()
        {
            // 当第一个线程运行到这里时，此时会对locker对象 "加锁"，
            // 当第二个线程运行该方法时，首先检测到locker对象为"加锁"状态，该线程就会挂起等待第一个线程解锁
            // lock语句运行完之后（即线程运行完之后）会对该对象"解锁"
            // 双重锁定只需要一句判断就可以了
            if (_uniqueInstance == null)
            {
                lock (locker)
                {
                    // 如果类的实例不存在则创建，否则直接返回
                    if (_uniqueInstance == null)
                    {
                        _uniqueInstance = new TwinCheckSingleton();
                    }
                }
            }
            return _uniqueInstance;
        }
    }
}
